# import panda main module
import direct.directbase.DirectStart

from pandac.PandaModules import *
# module for task controlling
from direct.task import Task

class Camera():
    def __init__(self, map):
        # map istance
        self.map = map

        # range of movement
        self.hMin = 11
        self.hMax = 260
        
        # metres of elevation change every zoom
        self.zoom = 40
        
        # camera movement in metres
        self.totalMovement = 0
        # deceleration in percent every loops
        # the sum must be 100
        self.deceleration = [10, 10, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1]
        # function that updates the camera
        self.moveFunction = ''
        # current loop
        self.currentTime = 0
        # movement already done
        self.doneMovement = 0
        
        # camera vertical angle
        self.verticalAngle = -40
        # camera horizontal angle
        self.horizontalAngle = 0
        # camera height
        self.height = 50
        # terrain height under the camera
        self.terrain_height = 0
        # camera position
        self.x = 40
        self.y = 30
        
        # camera
        
        # window size
        self.window_x = base.win.getProperties().getXSize()
        self.window_y = base.win.getProperties().getYSize()
        
        # set the camera
        self.set_camera()
        # loop that controls the mouse events
        taskMgr.add(self.mouse_event, "mouse")
        
        
    # method that updates camera values
    def set_camera(self):
        base.camera.setPos(self.x, self.y, self.height+self.terrain_height)
        base.camera.setHpr(self.horizontalAngle, self.verticalAngle, 0)
    
    # to move the camera
    def move(self, x, y):
        # control if the camera goes out of the map limits
        if self.x + x > -self.map.info.width/2:
            if self.x + x < self.map.info.width/2:
                self.x += x
            else:
                self.x = self.map.info.width/2
        else:
            self.x = -self.map.info.width/2
            
        if self.y + y > -self.map.info.height/2:
            if self.y + y < self.map.info.height/2:
                self.y += y
            else:
                self.y = self.map.info.height/2
        else:
            self.y = -self.map.info.height/2
        
        # elevation of the terrain under the camera
        self.terrain_height = self.map.elevation(self.x, self.y)
        
        # set the camera
        self.set_camera()
    
    def rotate(self, angle):
        self.horizontalAngle += angle
        
        # set the camera
        self.set_camera()
    
    def inclination(self, angle):
        if self.verticalAngle + angle < -90:
            self.verticalAngle = -90
        elif self.verticalAngle + angle > 10:
            self.verticalAngle = 10
        else:
            self.verticalAngle += angle
        
        # set the camera
        self.set_camera()
    
    # direction can be 'up' or 'down'
    # up => zoom out
    # down => zoom in
    def camera_zoom(self, direction):
        """*Move* the camera up and down (it's not properly a zoom), and rotate it.
        """
        #Store direction to be used from self.velocity
        self.direction = {'out':1, 'in':-1}[direction]
        
        if self.currentTime == 0:
            # zoom out if camera height is lower than 260 m
            if direction == 'out' and self.height < self.hMax:
                self.totalMovement = self.zoom
                self.moveFunction = self.change_zoom
                    
                taskMgr.remove('ZoomCamera')
                taskMgr.add(self.velocity, 'ZoomCamera')
            # zoom in if camera is over than 10 m
            elif direction == 'in' and self.height > self.hMin:
                # camera velocity becomes negative
                self.totalMovement = -self.zoom
                self.moveFunction = self.change_zoom
                    
                taskMgr.remove('ZoomCamera')
                taskMgr.add(self.velocity, 'ZoomCamera')

    # set the mouse events
    def mouse_event(self, task):
        # abbreviation
        mwn = base.mouseWatcherNode
    
        # if the cursor exists
        if mwn.hasMouse():
            # control position on x axis
            if self.window_x - 5 < mwn.getMouseX() * self.window_x:
                messenger.send('MouseRight')
            elif 5 - self.window_x > mwn.getMouseX() * self.window_x:
                messenger.send('MouseLeft')
            
            # control position on y axis
            if self.window_y - 5 < mwn.getMouseY() * self.window_y:
                messenger.send('MouseUp')
            elif 5 - self.window_y > mwn.getMouseY() * self.window_y:
                messenger.send('MouseDown')
    
        return task.cont
    
    def change_zoom(self, movement):
        self.height += movement
        
        self.set_camera()
    
    def change_rotate(self, movement):
        self.horizontalAngle += movement
        
        self.set_camera()
    
    def velocity(self, task):
        speed = self.deceleration[self.currentTime]
        movement = (self.totalMovement / 100.0) * speed
        self.currentTime += 1
        
        self.moveFunction(movement)
        
        if self.currentTime < len(self.deceleration):
            # continue
            return task.cont
        else:
            # stop
            self.currentTime = 0
            return task.done
# references (temporary)
# http://www.physicsclassroom.com/Class/1DKin/U1L1d.cfm
